﻿using System;
using System.Windows.Media;
using System.Collections.Generic;
using System.IO;

using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Microsoft.Xna.Framework.Audio
{
	/// <summary>
	/// 
	/// </summary>
    public class SoundEffect : IDisposable
    {
		private static float distanceScale;
		private static float dopplerScale;
		private bool isDisposed;
        string _assetName;
        private static float volume = 0.5f;
        List<SoundEffectInstance> sounds = new List<SoundEffectInstance>();
        GraphicsDevice _graphics;
        static Dictionary<string, byte[]> soundBuffers = new Dictionary<string, byte[]>();
		private static float speedOfSound;

		#region Properties
		/// <summary>
		/// Gets or sets a value that adjusts the effect of distance calculations on the sound (emitter).
		/// </summary>
		public static float DistanceScale
		{
			get { return distanceScale; }
			set
			{
				if (value < 0f)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				value = (value <= float.Epsilon) ? float.Epsilon : value;
				distanceScale = value;
			}
		}

		/// <summary>
		/// Gets or sets a value that adjusts the effect of doppler calculations on the sound (emitter).
		/// </summary>
		public static float DopplerScale
		{
			get { return dopplerScale; }
			set
			{
				if (value < 0f)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				dopplerScale = value;
			}
		}

		/// <summary>
		/// Gets the duration of the SoundEffect.
		/// </summary>
		public TimeSpan Duration
		{
			get { throw new NotImplementedException(); }
		}

		/// <summary>
		/// Gets a value that indicates whether the object is disposed.
		/// </summary>
		public bool IsDisposed
		{
			get { return isDisposed; }
		}

		/// <summary>
		/// Gets or sets the master volume that affects all SoundEffectInstance sounds.
		/// </summary>
		public static float MasterVolume
		{
			get { return volume; }
			set { volume = value; }
		}

		/// <summary>
		/// Gets or sets the asset name of the SoundEffect.
		/// </summary>
		public string Name
		{
			get { return this._assetName; }
			set
			{
				if (this.IsDisposed)
				{
					throw new ObjectDisposedException(base.GetType().Name, FrameworkResources.ObjectDisposedException);
				}
				if (string.IsNullOrEmpty(value))
				{
					throw new ArgumentNullException("value");
				}
				this._assetName = value;
			}
		}

		/// <summary>
		/// Returns the speed of sound: 343.5 meters per second.
		/// </summary>
		public static float SpeedOfSound
		{
			get { return speedOfSound; }
			set
			{
				if (value <= 0f)
				{
					throw new ArgumentOutOfRangeException("value");
				}
				speedOfSound = value;
			}
		}
		#endregion

		public SoundEffect(ContentManager content, string assetName)
        {
            _assetName = assetName;
            if (soundBuffers.ContainsKey(assetName) == false)
            {
                Stream s = content.GetAssetStream(_assetName, ".mp3");
                if (s == null)
                {
                    s = content.GetAssetStream(_assetName, ".wma");
                }
                if (s == null)
                {
                    s = content.GetAssetStream(_assetName, ".wav");
                }

                if (s == null)
                    throw new ContentLoadException("Could not load audio asset: " + assetName + "\r\nSilverBullet only supports MP3, WAV, and WMA audio assets.");

                byte[] b = new byte[s.Length];
                s.Read(b, 0, b.Length);
                s.Close();
                soundBuffers.Add(assetName, b);
            }
            _graphics = ((IGraphicsDeviceService)content.ServiceProvider.GetService(typeof(IGraphicsDeviceService))).GraphicsDevice;
        }

		~SoundEffect()
		{
			Dispose(false);
		}

		#region Protected Methods
		protected void Dispose(bool disposing)
		{
			isDisposed = true;
		}
		#endregion

		/// <summary>
		/// Creates a new SoundEffectInstance for this SoundEffect.
		/// </summary>
		public SoundEffectInstance CreateInstance()
		{
			SoundEffectInstance s = new SoundEffectInstance();
			s.MediaElement.SetSource(new MemoryStream(soundBuffers[_assetName]));
			s.Volume = volume;
			return s;
		}

		/// <summary>
		/// Releases the resources used by the SoundEffect.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Creates a SoundEffect object based on the specified data stream.
		/// </summary>
		/// <param name="stream">Stream object that contains the data for this <see cref="T:SoundEffect"/> object.</param>
		public static SoundEffect FromStream(Stream stream)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Plays a sound.
		/// </summary>
		public bool Play()
		{
			return Play(volume, 0.0f, 0.0f);
		}

		/// <summary>
		/// Plays a sound based on specified volume, pitch, and panning.
		/// </summary>
		/// <param name="volume">Volume, ranging from 0.0f (silence) to 1.0f (full volume). 1.0f is full volume relative to <see cref="P:SoundEffect.MasterVolume"/>.</param>
		/// <param name="pitch">Pitch adjustment, ranging from -1.0f (down one octave) to 1.0f (up one octave). 0.0f is unity (normal) pitch.</param>
		/// <param name="pan">Panning, ranging from -1.0f (full left) to 1.0f (full right). 0.0f is centered.</param>
        public bool Play(float volume, float pitch, float pan)
        {
			//TODO: Obviously sounds may have more than one media element...
			SoundEffectInstance s = null;
			foreach (SoundEffectInstance ele in sounds)
			{
				if (ele.MediaElement.CurrentState == MediaElementState.Stopped || ele.MediaElement.CurrentState == MediaElementState.Paused)
				{
					s = ele;
					break;
				}
			}

			if (s == null)
			{
				s = new SoundEffectInstance();
				sounds.Add(s);
				_graphics.Root.Children.Add(s.MediaElement);
				s.Pan = pan;
				s.Volume = volume;
			}
			s.MediaElement.SetSource(new MemoryStream(soundBuffers[_assetName]));
			s.MediaElement.Play();
			return true;
        }
    }
}
